更多相关文章阅读
认识高性能Web缓存体系,你需要知道这些
本文整理自 GOPS2017.北京站演讲实录《高性能Web架构之缓存体系设计与实践》
讲师简介:
赵舜东
中国SaltStack用户组发起人
江湖人称:赵班长,曾在武警某部负责指挥自动化的架构和运维工作,2008年退役后一直从事互联网运维工作,历任运维工程师、运维经理、运维架构师、运维总监。《SaltStack技术入门与实战》作者,《运维知识体系》作者,GOPS金牌讲师,Exin DevOps Master认证讲师。
前言
我们再看知识体系的时候,我们学一个东西的时候,每次我们都回过头去看一看,这就是所谓的不忘初心。这个说着容易做起来难,当一个人慢慢在成长,在进步的时候,是很难做到不忘初心的。
我们之前说了DNS缓存、浏览器缓存(维护了这么久的服务器,你真的认识 Web 缓存体系?),所以浏览器就是我们安排在千家万户缓存代理服务器,你把浏览器缓存用好,性能就不用说。
为什么这么说?如果遇到关于session或cookie的过期时间这样的问题,浏览器都不会向服务器发送连接请求。它直接用浏览器本地缓存就打开了,你说它快还是不快。
CDN与反向代理缓存
CDN缓存体系
我看了一下所谓浏览器之间的关于网络数据的缓存,可能也就是ARP解析缓存,其他Cache数据应该没有,数据只有Buffer。
为什么有Buffer?我有一个网卡发数据包的时候,我不可能一两个数据包发,我会有Buffer缓存数据包再一起发。我们DNS也有这样的配置,就是配置这样的TCP的Buffer配置。所以我后来研究了一下,网络层面几乎没有。所以用户层出来之后,就直接到了代理层。
我们的请求终于出了浏览器,不容易,一个请求在浏览器有这么多缓存。这个时候我们现在都用CDN,没有CDN的话一定是你的失职,因为现在CDN便宜得不得了,如果传统Web服务你能用的时候不用就是你工作失职。
借助腾讯云的一张图,我们看一下CDN的请求流程。我们可以看是DNS解析,到了腾讯DNS上,它会根据你的local DNS IP发挥最佳接入节点,返回一个IP地址访问CDN接入节点。
问题一:我们经常发现很多CDN调度不准确,为什么?
因为传统的智能DNS是无法获取用户IP的,它只能获取localIP,这时候他就获取不到对的IP地址了。他通过IP地址发起请求,首先到CDN接入节点,边缘节点,边缘节点都是集群。
问题二:如果边缘节点没有怎么办?
到CDN中间源相关存储,如果还没有回到用户的原站,取回数据,缓存,返回给用户,这是整个CDN请求的流程。
问题三:我们之前也有很多下载业务,下载业务基本上是不回源的,怎么办?
我每五分钟把数据同步到CDN用户上,不让它回源,因为我们发现CDN老回源,由于下载量很大的,我们带宽就不够,一回源就把带宽堵死了,这时候怎么办?
方式一:我定期给你同步,那你就要保证在请求之前数据要同步过去,这是一种方式。
方式二:下载站点切CDN的方式,我不知道大家怎么切,很多人可能是改CDN解析。
方式三:,URL动态生成,我们的URL是动态生成的,其实也不算动态生成。当然还有比如说缓存,应用程序本地缓存。
CDN关键技术
CDN关键技术,比如请求的调度,比如刚才的那种情况,IP地址不对怎么办,我还可以做第二次跳转,真正发起请求的时候,这个时候能获取到用户的真实IP。
反向代理缓存
我发现你的IP地址和我IP地址不对称,我给你返回一个CDN,重新下载等等。还有内容文件怎么分发,怎么回源,还有内容存储,这也是CDN重点的地方。再加上配置,比如通过配置管理的方式,自动化批量做边缘节点配置等等有很多。
CDN我们讲了半天,最重要的是反向代理缓存,目前反向代理缓存最主流的东西是ATS。像淘宝、阿里越来越多公司用ATS之后,现在很多也是采用ATS。但是现在主流做法不是单独用这个,都是在前面加上Nginx。
用Nginx+(Lua)在前端做相关七层的相关功能,满足到了下一个层面只做满层的功能。比如你要做安全、防攻击全在做这儿,你要做URL解析,压缩相关功能全在这儿做,最后到这儿整个缓存就可以了,这是目前主流的做法,就是让专业的工具干专业的事情。
所以这里面还有另一个关键,讲自动化运维的时候,有一句话叫做最好的一种架构,尽量的单一技术:负载均衡Nginx,Web服务器Nginx,反向代理缓存Nginx,你的公司只要招一个Nginx大牛,就解决了所有问题,这个真的挺好的。
小公司你觉得没那么明显,但是对于像BAT这样的量级,对于他们来说很明显,为什么?量级太大,如果技术组件泛滥,那就没法玩了。
昨天腾讯大梁分享咱们有听到,几十万台服务器,一个用这个,一个用那个就没法玩,这时候就标准化,接入层用什么,你想用什么我做二次开发满足你的需求,这就是单一标准化技术架构的好处。
我单独截取了几个Nginx反向代理缓存的,这个目录就可以通过挂载tmpfs来做,就不用写硬盘了。当然还有缓存刷新,一般CDN会提供缓存刷新芯片,你可以批量的把你过期的资源全部刷新一遍,这是一种方式。
Web服务器与分布式缓存
Web服务器缓存
我们把用户层和代理层所有缓存都已经讲完了,再往下我们要讲Web服务器。我现在这个请求到达了Web服务器上。
Web服务器有什么缓存?我们先说第一种缓存,缓存动态内容输出,这个做开发的比较熟悉,很多开发框架都有内置缓存引擎,你可以去把一些动态内容输出直接缓存下来,这样的话再次获取的时候就非常方便,当然还可以支持SSI,但是这个慎用,因为对性能影响非常大。
页面静态化
是不是就有一个HTML5页面,这些HTML5页面保存在本地,可能要做一些修改,页面上的登录通过JS加载就可以了。
举个例子,像京东的产品详细页就是静态页面。频道页一般也是静态页面,频道页有单独的域名,还有它的产品详细页,这些页面都是静态页面,为什么?因为它的访问量很高,但是价格是另外再去加载的,所以这个页面就是通过CMS去生成它。
其实还有一个小细节,你会发现我们再打开京东页面,这些静态资源和主站,是在不同顶级域名下,大家知道为什么在不同的顶级域名下面吗?这不是偶然,就是要这样设计,这就涉及到cookie,静态资源需要cookie吗?当然不需要,但是你知道浏览器默认情况下会干什么。比如jd.com写了一个cookie,你只要访问所有京东的页面,都会带上cookie,这个时候影响带宽,影响性能。
这个时候我们需要把它放在单独的顶级域名下面,所以你要记住你的主站和静态资源在做组件分离的时候,一定要使用单独的顶级域名,不要二级域名,没有防止cookie提交,性能会产生影响。
还有做页面组件CDN方式也有很多好处,你可以做到针对不同的资源,配置不同的Web服务器。举个例子,大图片和小图片的存储,性能调优,机器能一样吗?不一样,这时候大图片,小图片就会使用不同的域名来处理这样的关系。然后在大图片集群下用什么技术,在小图片集群下用什么技术,都可以把它分开,这是页面静态化的案例。
页面静态化怎么生成呢?其实一般会有两种主流生成方式:
第一种生成方式,循环生成
比如所有的商品页面,我每隔几个小时把所有商品页面塞到一个队列里面,生成一个少一个,取一个生成一个。第二种生成方式,队列生成
这个队列要发挥优先级,有高优先级队列和低优先级队列,为什么要划优先级?因为第二种页面生成方式,我作为运维人员在后台修改一个页面,比如页面描述有一些错了,这时候你就不能等定制生成,这时候应该往高优先级队列插一条数据,先把这个页面重新静态化,但页面静态化是最解决问题的方法。第三种生成方式,搜索生成
能不能静态化?也可以的,因为我们之前在做电商的时候,遭遇过搜索攻击,什么意思呢?别人知道你们这个搜索一定是动态的,怎么办呢?我不停搜你就搜乱了,我们至少要保证把常用的搜索关键字全部做成静态页面,所以你点这些常用搜索就是静态页面,我会定期静态,定期生成,当然还有别的做法。比如搜索要和主站分开,最早搜索又不支持高可用,后来更换以后才变好的。
应用代码执行缓存
下面我们讲应用代码执行缓存,我们都知道我们语言一般分为两种:编译型语言和解释型语言。
编译型语言可以直接编译成二进制代码,直接在Web服务器上运行。
解释型语言比如PHP脚本,要先解析成中间操作码,然后在解释型引擎上再来做运行,那这个中间码也是可以做缓存的。
同样一串PHP脚本,每次解析出来都是一样的,我们就没有必要每次频繁做解析了,这个是可以做缓存的。
说到缓存还涉及到一个问题:安全。比如怎么检测Web木马,很多人回答说做一个扫描器,把所有文件扫一遍,看看有没有木马关键字。不行?这是最简单的方式,因为人家可以变。黑客一直在变,通过扫描器不行。这时候还有一种办法,通过中间码的方式来做Web木马扫描。
PHP OpCache
这里举一个例子,PHP操作码缓存,PHP脚本执行的时候。
首先会检测OpCache,操作码缓存有没有,如果有直接执行。如果没有解析,编译成中间码,然后保存到共享内存里,然后再执行。这样的话,下一次再执行PHP脚本的时候,就会有缓存了。
有人问我说这个缓存能带来多少性能提升呢。我有一个生产案例,我们一个广告API,每天PV至少过亿,当时CPU用户使用率大概高峰一般在70%左右,已经很高了。
使用了OPCache之后,CPU使用率降到百分之三四十,CPU使用率直接降低一半,性能不用说了。我们每个API响应时间要控制在100毫秒以内,当然其他的场景可能会更低。
因为对我们来说100毫秒就OK了,因为涉及业务还比较复杂。那OpCache怎么用呢?在PHP5.5版本之前,你需要通过APC的方式扩展来开启OpCache,在PHP5.5版本之后,只需要:
编译时增加—enable-opcache
修改php.ini增加zend_extension=/full/path/to/opcache.so
修改php.ini配置相关参数。
PHP OpCache缓存
剩下的是应用程序本地缓存,其实还有一个没有讲到,现在用得少。比如缓存动态内容输出,除了框架有这个功能,Web服务器也有。他们会缓存一些URL预设等等,我没有讲到是因为默认是开启的,你也不需要调整,但是你要知道Web服务器都有了。
还有是PEPFastCGI缓存,这个缓存我们也是可以做的。还有一个缓存是应用程序本地缓存,本如做Java的EhCache,可以让你把数据放在本地内存里,这个对性能提升也很大。因为我们之前业务过度依赖Redis,所以Redis经常压力会很大。
这时候我们把缓存体系路径拉出来,我们来考虑一下到底要不要把数据写在Redis里。我们把本地使用缓存可以直接存到缓存中,就不用Redis了,这个对性能提升也很大。
Memcached 数据库读缓存
我们再回过头来看一下缓存知识体系,应用服务可以做缓存动态内容输出,页面静态化,localCache。请求通过浏览器、CDN、Web服务器解释器缓存,应用服务缓存,都没有命中,没关系,我们后面还有一大堆缓存等着它。
我们为什么要将缓存一级级往后呢,我们让请求尽量离用户最近,最快,最少资源能够让用户获取到。因为越往下,成本越高。
访问数据库当然是最慢的了,这时候说不行,这些缓存全部没有命中,我要查数据库,这时候怎么办呢?
等等先不要查数据库,因为查数据库太慢,先查分布式缓存。比如MemCached我们做数据库读缓存,能不能做到写缓存呢?
可以做到,但是很少用MemCached写缓存的。比如我们对用户注册进行修改,以前用户注册肯定走数据库,当大促推广的时候用户注册扛不住,这时候写在消息队列里面直接返回,我会从消息队列读出来,最终把消息写在数据库里,其实大的公司都这么干。
举个例子,下订单不管在哪儿下订单,下完订单点查看订单,告诉你订单还在处理中,不能查看,实际上这个订单还没有写在数据库里,你现在看不了。但是现在我们用MemCached最主要还是做数据库读缓存。
再说一个但是,现在已经不用MemCached了,因为有了Redis之后,MemCached有点鸡肋了,除非是纯缓存,这时候MemCached性能是要超越Redis的。
但是有了Redis以后,我们后来就不用MemCached,用MemCached还要维护它,很麻烦,我直接改成Redis就可以了,对于开发来说就是改几行代码,批量查找切换,就可以切换到Redis上。而且Redis支持不同数据类型,Redis不仅可以做缓存,还可以做存储。
像我们Redis有两个功能,一个是做数据库读缓存,还有一个最重要功能就是存储,很多数据直接写在Redis里,所以Redis不能挂。不能挂就涉及到集群问题,挂了怎么办,Redis集群有哪些种?
Redis做数据库读缓存
首先客户端分片,这是最简单的集群模式,比如说四个Redis你爱写哪儿写哪儿,它会比较灵活,自己可掌控。但是有一个很大的缺点,你要加和删这就费劲了,要是缓存还好,加删有很少的数据Cache丢失。
但是如果你不是在缓存,是做数据存储,这时候涉及到数据迁移,还只能手动迁移,这就比较费劲了。第二种解决方案是通过Proxy做分片,比如Twemproxy。但是还是有一个问题,数据迁移没有,还有Redis Cluster,但是Redis Cluster对用户来说不是透明的。之前用Redis,现在切换到Redis Cluster,各种东西都要改的,这是一个大的问题。
还有目前比较新,应用案例相对来说不是很多。最后讲Codis,Codis之前豌豆荚开源了一个,但是那帮人大部分已经离职了。现在在gataApp上专门有一个项目做Codis,Codis方案怎么做呢?有点类似于客户端分片,首先分0—1663,总共分1664个槽位,把相应的K放在不同槽位上,再把槽位放在的后端不同的Redis服务器上。
但是它有一个好的功能是,它对Redis服务器做了二次开发,叫做Codis服务器。做了二次开发支持做数据迁移,比如我想把0—500槽位迁到另外一台Redis主从的集群上,没问题,鼠标点一下就可以迁移,而且是不中断迁移,这就是它的好处。
而且Codis提供一个Redis-Port,这个工具可以无缝的把Redis迁移到Codis上。它的工作原理是:通过Redis-Port这个工具连到RedisMaster上,把自己模拟成一个Redis Slave,我是你的从,你给我做数据同步。
然后master很自觉的做一个BGS,然后把RBG文件发解Slave,Redis-Port把它解析出来,其实是解析出来成命令了,再做Codis上执行,通过这样的方式,把Redis迁到Codis上。
我之前经历过把几个Redis迁到Codis上,就是用Redis-Port工具非常方便。现在我们很多业务主流都是Codis,不再单纯的用Redis了,而且Codis每个group都有一主多从,从挂了主可以自动替上。而且Codis有一个Proce,这个Proce对于用户来说是拓扑的,连多Proce和连到Redis上是一样的,什么都不用改。
之前用的什么模块现在还用什么模块,什么都不用改。它是无感知的,但是它也有一个缺点,有一些命令是不支持的,不支持的命令都是非常不常用的命令。
数据库与操作系统缓存
因为我们只讲缓存,没有讲架构,架构层面这里面还有很多其他的内容,下面的请求经历各种缓存,终于到达数据库,实在没有办法,都没有命中,这时候数据库也有缓存。
Oracle体系结构
我们先看Oracle,Oracle写在SGA里有Data Buffer Cache,所有内存读写都是在内存做的,但是还有redo log是写在Buffer里面的。
MySQL缓存
MySQL也是一样的,对于运维来说改改配置文件就好了。但是你改配置文件之前要理解一下缓存是怎么用的,这是我们需要理解的。不是说改完配置文件缓存做完了,这是不行的。这个时候请求继续往下走就走到操作系统层面了,那就涉及到操作系统缓存,CPU、内存、磁盘。
操作系统缓存之CPU
首先CPU缓存,我们知道CPU有一二三级缓存。一级缓存又被分为一级指令缓存和数据缓存,所以大家买CPU的时候,要看一看CPU缓存大小,缓存对CPU执行性能是有很大关系的。
L2、L3,L3很多时候是共享的,一个CPU多个核,各个核之间是共享的,所以存取的时候,这个缓存既有Buffer功能,也有Cache功能,最后读写从内存中来。
咱们做运维做性能调优有没有用过CPU绑定?你们配置NJS如果用过,那个其实就是做进程绑定的。把NJS进程绑定到CPU某个核上,绑定的作用是提高CPUCache命中率,因为我们的进程在操作系统运行是受进程调度器控制。
你在CPU一个核上运行以后,如果中断,下一次再运行的时候,可能就在2上,这个时候缓存如果不是共享的,缓存就命中不了,所以我们通过这种把进程绑定到某一个CPU核上,来减少CPU的Cache Miss,来提高性能。
举个例子,我们做虚拟化KVM,我们把KVM进程绑定到CPU核上,和你不绑定到CPU核上,大概性能影响是,没绑之前可能占70%的CPU,绑之后性能大概提升10%左右,因为减少了CPUCache Miss。当然做绑定就丧失了灵活性,但是NJS这样是可以的,它默认也支持。
下面是内存,内存有一个Page Cache,一个是Buffer Cache,Page Cache主要是针对文件层,Buffer Cache是针对块设备的。我们I/O使用,读写是这么做的,我特意把路径写出来了。磁盘块到Buffer Cache到Page Cache到应用程序进程空间。
Linux IO 架构
再看一下I/O,我们现在I/0调度算法是三种,最早是四种,还有一种系AS一种算法,现在是三种。
而且现在都支持多队列写入,这时候我们作为运维工程师调优涉及到I/O调度算法的调优,默认情况下用截止时间没问题。
但是如果你的硬盘是SSD,这时候最好把I/O调度算法调优,就是不做任何调度,按正常的。你先来就先存取,为什么?因为SSD足够得快,你没必要再做调度。I/O里面也有一些缓存,其实也不多。
现在数据经过CPU、磁盘,这时候就涉及到读取了,读取别着急,还有缓存。
RAID缓存
首先是RAID卡缓存,这是一个730P的RAID卡,它的缓存容量是2G。RAID卡缓存一般也是有Buffer、Cache两种功能,像它的Buffer很容易理解,写数据的时候先写到Buffer RAID卡,Cache有预读功能,来提高访问速度。而且RAID卡还有一个好处,这个有电池,所以你即使机器断电没有关系,有电池,数据不会丢。RAID卡写完之后到硬盘上,你会发现现在很多的服务器必须配RAID卡,所有数据必须经过RAID卡才能往下写,就是因为RAID卡有电池。
磁盘缓存
这是一个很普通的戴尔SAS,600G容量,2.5英寸,64兆缓存。写的时候写到64兆,读的时候从64兆里面读。
这时候就有问题的,我有RAID卡缓存,有硬盘缓存,硬盘缓存开还是不开?这个缓存开还是不开是RAID卡控制的,默认是关闭的。
我之前用戴尔系列服务器,默认是关闭的,在服务器上默认磁盘缓存是关闭的,只用RAID卡缓存,这才多大,还是RAID卡大。那个人笔记本可以选择打开或者关闭,这是磁盘缓存。然后获取到数据,一步一步再回去。
总结
我们整体看一下缓存体系结构,一个请求从用户层进来,一直到最后,到物理层,经历所有的缓存全在这里。我们只讲读缓存,写缓冲我们并没有列举。
我们做一个整体回顾,DNS浏览器缓存协商,CDN反向代理缓存,解释器OpCache,Web服务器缓存,动态内容缓存,页面静态化,local Cache,分布式缓存,MySQL、CPU Cache、内存Cache、RAID卡Cache、Disk Cache。
END
才貌双全的赵班长即将现身GOPS2017.上海站
传授你全开源端到端部署流水线设计和建设过程的宝贵经验
GOPS2017.上海站,赵班长等着你
独乐乐不如众乐乐,DevOps 时代社区长期欢迎原创作者投稿,DevOps 时代社区愿陪伴您共同成长。投稿邮箱:liuce@greatops.net
点击阅读原文关注GOPS2017.上海站活动官网